package de.jmda.core.mproc.task;

import static de.jmda.core.mproc.ProcessingUtilities.asTypeElement;
import static de.jmda.core.mproc.ProcessingUtilities.getRootElements;
import static de.jmda.core.mproc.ProcessingUtilities.normalizeBinaryClassName;
import static de.jmda.core.util.CollectionsUtil.asSet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;

import org.junit.Test;

import de.jmda.core.mproc.ProcessingUtilities;
import de.jmda.core.mproc.task.AbstractTypeElementsTaskTypes;
import de.jmda.core.mproc.task.TaskException;
import de.jmda.core.mproc.task.TaskRunner;
import de.jmda.core.util.fileset.FileFilterJavaSourceFilesOnly;
import de.jmda.core.util.fileset.FileSet.RuleSetUpdateMode;

/**
 * Tests basic functionality of {@link AbstractTypeElementsTaskTypes}.
 *
 * @author roger@jmda.de
 */
public class JUTTypeElementsTaskTypes
{
	/**
	 * Test implementation of {@link AbstractTypeElementsTaskTypes}, collects
	 * qualifiedNames of all type elements available via {@link
	 * #getTypeElements()} and {@link ProcessingUtilities#getRootElements()}.
	 */
	private class TypeElementsTaskTypes extends AbstractTypeElementsTaskTypes
	{
		private List<String> qualifiedNamesTypeElements = new ArrayList<>();
		private List<String> qualifiedNamesRootTypeElements = new ArrayList<>();

		public TypeElementsTaskTypes(Set<? extends Class<?>> types)
    {
	    super(types);
    }

		@Override
		public boolean execute() throws TaskException
		{
			for (TypeElement type : getTypeElements())
			{
				qualifiedNamesTypeElements.add(type.getQualifiedName().toString());
			}

			for (Element element : getRootElements())
			{
				TypeElement typeElement = asTypeElement(element);
				
				if (typeElement != null)
				{
					qualifiedNamesRootTypeElements.add(typeElement.getQualifiedName().toString());
				}
			}

			return false;
		}
	}

	/**
	 * Tests {@link AbstractTypeElementsTaskTypes} with a type only available as a
	 * .class byte code file in a .jar file in the classpath.
	 *
	 * @throws IOException
	 */
	@Test
	public void isTopLevelTypeFromJarInTypeElements() throws IOException
	{
		TypeElementsTaskTypes task = new TypeElementsTaskTypes(asSet(String.class));

		TaskRunner.run(task);

		assertTrue(
				"missing top level type from jar",
				task.qualifiedNamesTypeElements.get(0).equals(String.class.getName()));
		assertEquals(
				"wrong number of top level types from jar",
				1, task.qualifiedNamesTypeElements.size());
	}

	/**
	 * Tests {@link AbstractTypeElementsTaskTypes} with a top level type only
	 * available as a .class byte code file in the classpath.
	 *
	 * @throws IOException
	 */
	@Test
	public void isTopLevelTypeFromRuntimeInTypeElements() throws IOException
	{
		TypeElementsTaskTypes task =
				new TypeElementsTaskTypes(asSet(JUTTypeElementsTaskTypes.class));

		TaskRunner.run(task);

		assertTrue(
				"missing top level type from runtime",
				task.qualifiedNamesTypeElements.get(0).equals(
						JUTTypeElementsTaskTypes.class.getName()));
		assertEquals(
				"wrong number of top level types from runtime",
				1, task.qualifiedNamesTypeElements.size());
	}

	/**
	 * Tests {@link AbstractTypeElementsTaskTypes} with an inner type only
	 * available as a .class byte code file in the classpath.
	 *
	 * @throws IOException
	 */
	@Test
	public void isInnerLevelTypeFromRuntimeInTypeElements() throws IOException
	{
		TypeElementsTaskTypes task =
				new TypeElementsTaskTypes(asSet(TypeElementsTaskTypes.class));

		TaskRunner.run(task);

		assertTrue(
				"missing inner type from runtime",
				task.qualifiedNamesTypeElements.get(0).equals(
						normalizeBinaryClassName(TypeElementsTaskTypes.class)));
		assertEquals(
				"wrong number of inner level types from runtime",
				1, task.qualifiedNamesTypeElements.size());
	}
	
	@Test
	public void testRootElements() throws IOException
	{
		TypeElementsTaskTypes task =
				new TypeElementsTaskTypes(asSet(TypeElementsTaskTypes.class));

		task.getCustomRootFiles().includeDirectoryRecursive(
				new File("src/test/java"),
				new FileFilterJavaSourceFilesOnly(),
				RuleSetUpdateMode.ADD_RULE);

		TaskRunner.run(task);

		for (String rootTypeName : task.qualifiedNamesRootTypeElements)
		{
			System.out.println("root type name: " + rootTypeName);
		}

		assertTrue(
				"too few root type elements " + task.qualifiedNamesRootTypeElements.size(),
				task.qualifiedNamesRootTypeElements.size() > 1);
	}
}